package host.springboot.framework.mybatisplus.generate;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.builder.Controller;
import com.baomidou.mybatisplus.generator.config.builder.Entity;
import com.baomidou.mybatisplus.generator.config.builder.Mapper;
import com.baomidou.mybatisplus.generator.config.builder.Service;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.model.ClassAnnotationAttributes;
import host.springboot.framework.core.util.Assert;
import host.springboot.framework.mybatisplus.domain.BaseAssignDO;
import host.springboot.framework.mybatisplus.domain.BaseDO;
import host.springboot.framework.mybatisplus.enumeration.error.MybatisGenerateErrorEnum;
import host.springboot.framework.mybatisplus.exception.MybatisGenerateException;
import host.springboot.framework.mybatisplus.generate.config.CodeBasicConfig;
import host.springboot.framework.mybatisplus.generate.config.CodeDataSourceConfig;
import host.springboot.framework.mybatisplus.generate.config.CodePackageConfig;
import host.springboot.framework.mybatisplus.service.BaseService;
import host.springboot.framework.mybatisplus.service.BaseServiceImpl;
import org.jspecify.annotations.NonNull;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

/**
 * MybatisPlus代码生成器
 *
 * <p>该类为 [MybatisPlus] 代码生成器服务, 自动将数据库表生成为 [Java] 代码
 * <p>Danger: 代码生成器将自动覆盖原有文件, 推荐在生成之前做好代码备份,
 * 如只需自动生成指定表则设置 {@link CodeDataSourceConfig#setIncludeTables(String...)} 即可
 * <hr>
 * <p><a href="https://baomidou.com/guides/new-code-generator">点击查看官方文档</a>
 *
 * @author JiYinchuan
 * @see CodeBasicConfig
 * @see CodeDataSourceConfig
 * @see CodePackageConfig
 * @since 1.0.0
 */
public class DefaultGenerateCodeServiceImpl implements MybatisGenerateCodeService {

    @Override
    public @NonNull FastAutoGenerator createGenerator(
            @NonNull CodeBasicConfig basicConfig,
            @NonNull CodeDataSourceConfig dataSourceConfig,
            @NonNull CodePackageConfig packageConfig)
            throws MybatisGenerateException {
        Assert.notNull(basicConfig, "MybatisGenerate basicConfig must not be null");
        Assert.notNull(dataSourceConfig, "MybatisGenerate dataSourceConfig must not be null");
        Assert.notNull(packageConfig, "MybatisGenerate packageConfig must not be null");

        if (dataSourceConfig.getUrl() == null || dataSourceConfig.getUrl().isEmpty()) {
            throw new MybatisGenerateException(MybatisGenerateErrorEnum.DATA_SOURCE_URL_IS_BLANK);
        }
        if (dataSourceConfig.getUsername() == null || dataSourceConfig.getUsername().isEmpty()) {
            throw new MybatisGenerateException(MybatisGenerateErrorEnum.DATA_SOURCE_USERNAME_IS_BLANK);
        }
        if (dataSourceConfig.getPassword() == null || dataSourceConfig.getPassword().isEmpty()) {
            throw new MybatisGenerateException(MybatisGenerateErrorEnum.DATA_SOURCE_PASSWORD_IS_BLANK);
        }
        if (packageConfig.getOutputPackage() == null || packageConfig.getOutputPackage().isEmpty()) {
            throw new MybatisGenerateException(MybatisGenerateErrorEnum.OUTPUT_PACKAGE_IS_BLANK);
        }

        String url = dataSourceConfig.getUrl();
        String username = dataSourceConfig.getUsername();
        String password = dataSourceConfig.getPassword();
        return FastAutoGenerator.create(url, username, password)
                .globalConfig(initGlobalConfig(basicConfig, packageConfig))
                .packageConfig(initPackageConfig(packageConfig))
                .injectionConfig(initInjectionConfig(basicConfig))
                .strategyConfig(initStrategyConfig(basicConfig, dataSourceConfig));
    }

    /**
     * 初始化全局配置
     *
     * <p><a href="https://baomidou.com/reference/new-code-generator-configuration/#%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE-globalconfig">点击查看官方文档</a>
     *
     * @param basicConfig   基本配置
     * @param packageConfig 包配置
     * @return 全局配置
     * @since 1.0.0
     */
    private @NonNull Consumer<GlobalConfig.@NonNull Builder> initGlobalConfig(
            @NonNull CodeBasicConfig basicConfig,
            @NonNull CodePackageConfig packageConfig) {
        return builder -> {
            // 禁止自动打开输出目录
            // builder.disableOpenDir();
            // 指定代码生成的输出目录
            builder.outputDir(packageConfig.getOutputDir());
            // 设置作者名
            builder.author(basicConfig.getAuthor());
            // 开启 Kotlin 模式
            // builder.enableKotlin();
            // 时间策略
            // builder.dateType(DateType.TIME_PACK);
            // 开启 springdoc 模式
            if (basicConfig.isEnableSpringdoc()) {
                builder.enableSpringdoc();
            }
            // 设置注释日期格式
            // builder.commentDate("yyyy-MM-dd");
        };
    }

    /**
     * 初始化包配置
     *
     * <p><a href="https://baomidou.com/reference/new-code-generator-configuration/#%E5%8C%85%E9%85%8D%E7%BD%AE-packageconfig">点击查看官方文档</a>
     *
     * @param packageConfig 包配置
     * @return 包配置
     * @since 1.0.0
     */
    private @NonNull Consumer<PackageConfig.@NonNull Builder> initPackageConfig(
            @NonNull CodePackageConfig packageConfig) {
        return builder -> {
            // 设置父包名
            builder.parent(packageConfig.getOutputPackage());
            // 设置父包模块名
            builder.moduleName(packageConfig.getParentModelName());
            // 设置 Entity 包名
            builder.entity(packageConfig.getEntityPackageName());
            // 设置 Service 包名
            builder.service(packageConfig.getServicePackageName());
            // 设置 Service Impl 包名
            builder.serviceImpl(packageConfig.getServiceImplPackageName());
            // 设置 Mapper 包名
            builder.mapper(packageConfig.getMapperPackageName());
            // 设置 Mapper XML 包名
            builder.xml(packageConfig.getMapperXmlPackageName());
            // 设置 Controller 包名
            builder.controller(packageConfig.getControllerPackageName());
            // 设置路径配置信息
            // builder.pathInfo(Collections.singletonMap(OutputFile.xml, "D://"));
        };
    }

    /**
     * 初始化注入配置
     *
     * <p><a href="https://baomidou.com/reference/new-code-generator-configuration/#%E6%B3%A8%E5%85%A5%E9%85%8D%E7%BD%AE-injectionconfig">点击查看官方文档</a>
     *
     * @param basicConfig 基本配置
     * @return 包配置
     * @since 1.0.0
     */
    private @NonNull Consumer<InjectionConfig.@NonNull Builder> initInjectionConfig(
            @NonNull CodeBasicConfig basicConfig) {
        Map<String, Object> injectionMap = new HashMap<>(16);
        injectionMap.put("since", basicConfig.getSince());
        return builder -> builder
                // 输出文件之前执行的逻辑
                // .beforeOutputFile()
                // 自定义配置模板文件
                // .customFile()
                // 自定义配置 Map 对象
                .customMap(injectionMap);
    }

    /**
     * 初始化策略配置
     *
     * <p><a href="https://baomidou.com/reference/new-code-generator-configuration/#%E7%AD%96%E7%95%A5%E9%85%8D%E7%BD%AE-strategyconfig">点击查看官方文档</a>
     *
     * @param basicConfig      基本配置
     * @param dataSourceConfig 数据源配置
     * @return 策略配置
     * @since 1.0.0
     */
    private @NonNull Consumer<StrategyConfig.@NonNull Builder> initStrategyConfig(
            @NonNull CodeBasicConfig basicConfig,
            @NonNull CodeDataSourceConfig dataSourceConfig) {
        return builder -> {
            // 初始化策略基础配置
            initStrategyBaseConfig(builder, dataSourceConfig);

            // 初始化 Entity 策略配置
            initStrategyEntityConfig(builder.entityBuilder());

            // 初始化 Controller 策略配置
            initStrategyControllerConfig(builder.controllerBuilder());

            // 初始化 Service 策略配置
            initStrategyServiceConfig(builder.serviceBuilder());

            // 初始化 Mapper 策略配置
            initStrategyMapperConfig(builder.mapperBuilder());

            if (basicConfig.getOutputType() == CodeBasicConfig.OutputType.ONLY_DOMAIN) {
                builder.controllerBuilder().disable();
                builder.serviceBuilder().disable();
                builder.mapperBuilder().disable();
            }
            if (basicConfig.getOutputType() == CodeBasicConfig.OutputType.ALL_EXCLUDE_DOMAIN) {
                builder.entityBuilder().disable();
            }
        };
    }

    /**
     * 初始化策略基础配置
     *
     * @param builder          策略配置
     * @param dataSourceConfig 数据源配置
     * @since 1.0.0
     */
    private void initStrategyBaseConfig(
            StrategyConfig.@NonNull Builder builder,
            @NonNull CodeDataSourceConfig dataSourceConfig) {
        // 开启大写命名, 默认值: false
        // builder.enableCapitalMode();
        // 开启跳过视图, 默认值: false
        // builder.enableSkipView();
        // 禁用 SQL 过滤, 默认值: true, 如果 SQL 过滤不支持, 可以关闭此选项
        // builder.disableSqlFilter();
        // 启用 schema, 默认值: false，多 schema 场景时启用
        // builder.enableSchema();
        // 模糊表匹配(SQL 过滤), 与 notLikeTable 互斥，只能配置一项
        // builder.likeTable();
        // 模糊表排除(SQL 过滤), 与 likeTable 互斥，只能配置一项
        // builder.notLikeTable();

        if (dataSourceConfig.getIncludeTables().length > 0) {
            // 增加表匹配(内存过滤), 与 addExclude 互斥，只能配置一项，支持正则匹配，如 ^t_.* 匹配所有以 t_ 开头的表名
            builder.addInclude(dataSourceConfig.getIncludeTables());
            // 增加表排除匹配(内存过滤), 与 addInclude 互斥，只能配置一项，支持正则匹配，如 .*st$ 匹配所有以 st 结尾的表名
            // builder.addExclude();
        }

        if (dataSourceConfig.getTablePrefix() != null && !dataSourceConfig.getTablePrefix().isEmpty()) {
            // 增加过滤表前缀
            builder.addTablePrefix(dataSourceConfig.getTablePrefix());
            // 增加过滤表后缀
            // builder.addTableSuffix();
        }

        // 增加过滤字段前缀
        // builder.addFieldPrefix();
        // 增加过滤字段后缀
        // builder.addFieldSuffix();
        // 内置模板输出文件处理
        // builder.outputFile();
    }

    /**
     * 初始化实体策略配置
     *
     * @param builder 策略配置
     * @since 1.0.0
     */
    private void initStrategyEntityConfig(Entity.@NonNull Builder builder) {
        // 设置 Java 实体模板
        // builder.javaTemplate(ConstVal.TEMPLATE_ENTITY_JAVA);
        // 名称转换实现
        // builder.nameConvert();
        // 设置父类
        builder.superClass(BaseAssignDO.class);
        // 禁用生成 serialVersionUID, 默认值: true
        builder.disableSerialVersionUID();
        // 覆盖已生成文件, 默认值: false
        // builder.enableFileOverride();
        // 开启生成字段常量, 默认值: false
        // builder.enableColumnConstant();
        // 开启链式模型, 默认值: false
        // builder.enableChainModel();
        // 开启 lombok 模型, 默认值: false
        builder.enableLombok();
        // 开启 Boolean 类型字段移除 is 前缀, 默认值: false
        builder.enableRemoveIsPrefix();
        // 开启生成实体时生成字段注解, 默认值: false
        builder.enableTableFieldAnnotation();
        // 开启 ActiveRecord 模型, 默认值: false
        // builder.enableActiveRecord();
        // 乐观锁字段名 (数据库), versionColumnName 与 versionPropertyName 二选一即可
        // builder.versionColumnName(BaseService.toUnderlineCase(BaseDO.FIELD_VERSION));
        // 乐观锁属性名 (实体), versionColumnName 与 versionPropertyName 二选一即可
        builder.versionPropertyName(BaseDO.FIELD_VERSION);
        // 逻辑删除字段名 (数据库), logicDeleteColumnName 与 logicDeletePropertyName 二选一即可
        // builder.logicDeleteColumnName(BaseService.toUnderlineCase(BaseDO.FIELD_LOGIC_DELETE));
        // 逻辑删除属性名 (实体), logicDeleteColumnName 与 logicDeletePropertyName 二选一即可
        builder.logicDeletePropertyName(BaseDO.FIELD_LOGIC_DELETE);
        // 数据库表映射到实体的命名策略, 默认下划线转驼峰命名: NamingStrategy.underline_to_camel
        builder.naming(NamingStrategy.underline_to_camel);
        // 添加父类公共字段
        builder.addSuperEntityColumns(BaseDO.FIELD_ID,
                BaseService.toUnderlineCase(BaseDO.FIELD_CREATE_TIME),
                BaseService.toUnderlineCase(BaseDO.FIELD_CREATE_USER),
                BaseService.toUnderlineCase(BaseDO.FIELD_UPDATE_TIME),
                BaseService.toUnderlineCase(BaseDO.FIELD_UPDATE_USER)
        );
        // 添加忽略字段
        // builder.addIgnoreColumns();
        // 添加表字段填充
        // builder.addTableFills();
        // 全局主键类型
        // builder.idType(IdType.ASSIGN_ID);
        // 转换文件名称
        // builder.convertFileName();
        // 格式化文件名称
        builder.formatFileName("%sDO");
        // 是否生成ToString方法, 默认为true
        // builder.toString(true);
        // 启用字段文档注释, 默认为true
        // builder.fieldUseJavaDoc(true);
        // 添加实体类注解
        // builder.addClassAnnotation();
        // 表注解处理器
        // builder.tableAnnotationHandler();
        // 字段注解处理器
        // builder.tableFieldAnnotationHandler();
        // 开启 Lombok 模型并设置Lombok注解
        builder.enableLombok(
                new ClassAnnotationAttributes("@Data", "lombok.Data"),
                new ClassAnnotationAttributes("@ToString(callSuper = true)", "lombok.ToString"),
                new ClassAnnotationAttributes("@EqualsAndHashCode(callSuper = true)", "lombok.EqualsAndHashCode")
        );
    }

    /**
     * 初始化控制器策略配置
     *
     * @param builder 策略配置
     * @since 1.0.0
     */
    private void initStrategyControllerConfig(Controller.@NonNull Builder builder) {
        // 设置 Controller 模板
        // builder.template(ConstVal.TEMPLATE_CONTROLLER);
        // 设置父类
        // builder.superClass();
        // 覆盖已生成文件, 默认值: false
        // builder.enableFileOverride();
        // 开启驼峰转连字符, 默认值: false
        // builder.enableHyphenStyle();
        // 开启生成 @RestController 控制器, 默认值: false
        builder.enableRestStyle();
        // 转换文件名称
        // builder.convertFileName();
        // 格式化文件名称
        // builder.formatFileName();
    }

    /**
     * 初始化服务策略配置
     *
     * @param builder 策略配置
     * @since 1.0.0
     */
    private void initStrategyServiceConfig(Service.@NonNull Builder builder) {
        // 设置 Service 模板
        // builder.serviceTemplate(ConstVal.TEMPLATE_SERVICE);
        // 设置 ServiceImpl 模板
        // builder.serviceImplTemplate(ConstVal.TEMPLATE_SERVICE_IMPL);

        // 设置 service 接口父类
        builder.superServiceClass(BaseService.class);
        // 设置 service 实现类父类
        builder.superServiceImplClass(BaseServiceImpl.class);
        // 覆盖已生成文件, 默认值: false
        // builder.enableFileOverride();
        // 转换 Service 接口文件名称
        // builder.convertServiceFileName();
        // 转换 Service 实现类文件名称
        // builder.convertServiceImplFileName();
        // 格式化 service 接口文件名称
        builder.formatServiceFileName("%sRepository");
        // 格式化 service 实现类文件名称
        builder.formatServiceImplFileName("%sRepositoryImpl");
    }

    /**
     * 初始化 Mapper 策略配置
     *
     * @param builder 策略配置
     * @since 1.0.0
     */
    private void initStrategyMapperConfig(Mapper.@NonNull Builder builder) {
        // 设置 Mapper 模板
        // builder.mapperTemplate(ConstVal.TEMPLATE_MAPPER);
        // 设置 MapperXml 模板
        // builder.mapperXmlTemplate(ConstVal.TEMPLATE_XML);

        // 设置父类
        builder.superClass(BaseMapper.class);
        // 覆盖已生成文件, 默认值: false
        // builder.enableFileOverride();
        // 标记 Mapper 注解
        builder.mapperAnnotation(org.apache.ibatis.annotations.Mapper.class);
        // 启用 BaseResultMap 生成, 默认值: false
        // builder.enableBaseResultMap();
        // 启用 BaseColumnList, 默认值: false
        // builder.enableBaseColumnList();
        // 设置缓存实现类
        // builder.cache();
        // 转换 Mapper 类文件名称
        // builder.convertMapperFileName();
        // 转换 XML 文件名称
        // builder.convertXmlFileName();
        // 格式化 mapper 文件名称
        builder.formatMapperFileName("%sMapper");
        // 格式化 xml 实现类文件名称
        builder.formatXmlFileName("%sMapper");
        // 自定义生成Mapper方法实现
        // builder.generateMapperMethodHandler();
    }
}
